package mobileMoney;

import java.io.IOException;
import java.io.FileWriter;
import java.util.*;
import java.text.*;
import sim.engine.SimState;
import sim.field.continuous.Continuous2D;
import sim.field.network.Network;
import sim.util.Bag;
import sim.util.Double2D;

public class ClientsJung extends SimState {
	public Continuous2D land = new Continuous2D(1.0, 100, 100);
	public int numClients = 2000;
	public int numCities = 7;
	public int maxNeighbor = 10;
	public int maxOtherNeighbor = 2;
	double randomMultiplier = 0.1;
	double transactionRate = 0.5;
	double upgradeAccountRate = 0.01;
	private ArrayList<Transaction> trans = new ArrayList<Transaction>();
	private ArrayList<TransactionType> types = new ArrayList<TransactionType>();

	public ArrayList<TransactionType> getTypes() {
		return types;
	}

	public void setTypes(ArrayList<TransactionType> types) {
		this.types = types;
	}

	public double getUpgradeAccountRate() {
		return upgradeAccountRate;
	}

	public void setUpgradeAccountRate(double upgradeAccountRate) {
		this.upgradeAccountRate = upgradeAccountRate;
	}

	public double getTransactionRate() {
		return transactionRate;
	}

	public void setTransactionRate(double transactionRate) {
		this.transactionRate = transactionRate;
	}

	public ArrayList<Transaction> getTrans() {
		return trans;
	}

	public void setTrans(ArrayList<Transaction> trans) {
		this.trans = trans;
	}

	public int getNumCities() {
		return numCities;
	}

	public void setNumCities(int numCities) {
		this.numCities = numCities;
	}

	public int getMaxNeighbor() {
		return maxNeighbor;
	}

	public void setMaxNeighbor(int maxNeighbor) {
		this.maxNeighbor = maxNeighbor;
	}

	public int getMaxOtherNeighbor() {
		return maxOtherNeighbor;
	}

	public void setMaxOtherNeighbor(int maxOtherNeighbor) {
		this.maxOtherNeighbor = maxOtherNeighbor;
	}

	public Network contacts = new Network(false);
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public ClientsJung(long seed) {
		super(seed);
		// TODO Auto-generated constructor stub
	}

	public double[] getClientsBalance() {
		Bag clients = contacts.getAllNodes();
		double[] distro = new double[clients.numObjs];
		for (int i = 0; i < clients.numObjs; i++)
			distro[i] = ((Client) (clients.objs[i])).getBalance();
		return distro;
	}

	public void generateTransactionTypes() {
		for (int i = 0; i < 100; i++) {
			TransactionType type = new TransactionType();
			type.setTypeCode(i);

			type.cyclic = random.nextBoolean(0.05);
			type.nature = random.nextBoolean(0.3);
			type.minVal = random.nextDouble();
			type.maxVal = Math.abs(35000 * random.nextGaussian());
			type.numPerCycle = random.nextInt(4);
			if (type.nature) {
				type.description = "Deposit from " + type.minVal + " to "
						+ type.maxVal;
			} else {
				type.description = "Withdraw from " + type.minVal + " to "
						+ type.maxVal;
			}
			types.add(type);
		}
	}

	public void start() {
		super.start();
		// clear land
		land.clear();

		// add clients
		int numInCity = numClients;
		int remainClients = numInCity;

		// create account profiles
		Profile p1 = new Profile(1, 16000, 2500, 6000, true, 16000);

		Profile p2 = new Profile(2, 35000, 35000, 35000, true, 35000);

		generateTransactionTypes();

		for (int c = 0; c < numCities; c++) {
			double cx = land.getWidth() * random.nextDouble();
			double cy = land.getHeight() * random.nextDouble();
			if (c == numCities - 1) {
				numInCity = remainClients;
			} else {
				numInCity = random.nextInt(remainClients / 2);
				remainClients -= numInCity;
			}

			for (int i = 0; i < numInCity; i++) {
				Client client = new Client();
				client.setLocation(c);
				if (random.nextDouble() > 0.9) {
					client.setProfile(p2);
				} else {
					client.setProfile(p1);
				}
				client.setBalance(random.nextLong((long) client.getProfile()
						.getMaxBalance()) + random.nextDouble());
				land.setObjectLocation(client,
						new Double2D(cx + random.nextDouble() - 0.5, cy
								+ random.nextDouble() - 0.5));
				contacts.addNode(client);
				schedule.scheduleRepeating(client);
			}
		}

		// define relationships

		Bag clients = contacts.getAllNodes();

		for (int i = 0; i < clients.size(); i++) {
			Object client = clients.get(i);

			// who has who as a contact
			Object clientB = null;
			// add internal network
			for (int j = 0; j < maxNeighbor; j++) {
				clientB = clients.get(random.nextInt(clients.numObjs));
				Client c1 = (Client) clientB;
				Client c2 = (Client) client;
				if (c1.getLocation() == c2.getLocation() & !c1.equals(c2)) {
					double likeliness = random.nextDouble();
					contacts.addEdge(client, clientB, new Double(likeliness));
				}
			}

			// add external network
			for (int j = 0; j < maxOtherNeighbor; j++) {
				clientB = clients.get(random.nextInt(clients.numObjs));
				Client c1 = (Client) clientB;
				Client c2 = (Client) client;
				if (c1.getLocation() != c2.getLocation() & !c1.equals(c2)) {
					double likeliness = random.nextDouble();
					contacts.addEdge(client, clientB, new Double(likeliness));
				}
			}

		}
	}

	public int getNumClients() {
		return numClients;
	}

	public void setNumClients(int numClients) {
		if (numClients > 0)
			this.numClients = numClients;
	}

	public double getRandomMultiplier() {
		return randomMultiplier;
	}

	public void setRandomMultiplier(double randomMultiplier) {
		if (randomMultiplier > 0)
			this.randomMultiplier = randomMultiplier;
	}

	public static void main(String[] args) {
		doLoop(Clients.class, args);
		System.exit(0);
	}

	public void finish() {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd_hhmm");
		try {
			String strDate = sdf.format(date);
			FileWriter aWriter = new FileWriter(
					"/home/edl/workspace/MobileMoneySim/logs/mm" + strDate
							+ ".arff", true);

			String types = "";
			for (int i = 0; i < 100; i++) {
				types = types + i;
				if (i != 99)
					types = types + ",";
			}

			String header = "@relation MobileMoney \n\n"
					+ "@attribute step NUMERIC\n"
					+ "@attribute client string\n"
					+ "@attribute profile {P1,P2}\n"
					+ "@attribute location string\n" + "@attribute typeTrans {"
					+ types + "}\n" + "@attribute amount NUMERIC\n"
					+ "@attribute balance NUMERIC\n"
					+ "@attribute clientB string\n"
					+ "@attribute profileB {P1,P2,null}\n"
					+ "@attribute locationB string\n"
					+ "@attribute balanceB NUMERIC\n\n"
					+ "@attribute suspicious {yes,no}\n\n" + "@data\n";
			aWriter.write(header);

			for (Transaction tran : trans) {
				aWriter.write(tran.getRecord() + ",no\n");
			}
			aWriter.flush();
			aWriter.close();
			// fh.close();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		try {
			String strDate = sdf.format(date);
			FileWriter aWriter = new FileWriter(
					"/home/edl/workspace/MobileMoneySim/logs/tt" + strDate
							+ ".arff", true);

			String typesCodes = "";
			for (int i = 0; i < 100; i++) {
				typesCodes = typesCodes + i;
				if (i != 99)
					typesCodes = typesCodes + ",";
			}

			String header = "@relation MobileMoney \n\n"
					+ "@attribute typeTrans {" + typesCodes + "}\n"
					+ "@attribute nature {true,false}\n"
					+ "@attribute minVal NUMERIC\n"
					+ "@attribute maxVal NUMERIC\n"
					+ "@attribute isCyclic {true,false}\n\n" + "@data\n";
			aWriter.write(header);

			for (TransactionType type : types) {
				aWriter.write(type.getRecord() + "\n");
			}
			aWriter.flush();
			aWriter.close();
			// fh.close();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
